// 添加时间格式化辅助函数（移到类外部）
function formatTime(seconds) {
    const minutes = Math.floor(seconds / 60);
    const remainingSeconds = seconds % 60;
    const milliseconds = Math.round((remainingSeconds % 1) * 1000);
    
    return `${String(minutes).padStart(2, '0')}:${String(Math.floor(remainingSeconds)).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}`;
}

class Metronome {
    constructor() {
        this.bpm = 120;
        this.timeSignature = '4/4';
        this.isPlaying = false;
        this.currentBeat = 0;
        this.intervalId = null;
        this.audioContext = null;

        // 初始化 ABCJS 合成器
        this.visualObj = null;
        this.synth = null;
        this.initABCJS();

        // 创建合成器控制元素
        this.createSynthControl();

        // 绑定 DOM 元素
        this.bpmInput = document.getElementById('bpm');
        this.timeSignatureSelect = document.getElementById('timeSignature');
        this.startStopButton = document.getElementById('startStop');

        // 绑定事件监听器
        this.bindEvents();
        
        // 初始显示节拍模式
        this.renderPattern();
        this.renderScore();

        // 添加日志容器
        this.createLogContainer();

        // 添加生成时间按钮的事件监听
        this.generateTimesButton = document.getElementById('generateTimes');
        this.generateTimesButton.addEventListener('click', () => this.generateExpectedTimes());

        this.startTime = null;  // 添加开始时间记录

        // 添加音频缓存
        this.strongBeatSynth = null;
        this.weakBeatSynth = null;
        
        // 预加载音频
        this.preloadAudio();

        // 添加打拍子相关的属性
        this.tapButton = document.getElementById('tapBeat');
        this.tapTimes = [];  // 存储点击时间
        this.tapStartTime = null;  // 打拍子的开始时间
        
        // 修改打拍子按钮的事件监听
        this.tapButton.addEventListener('mousedown', (e) => {
            e.preventDefault(); // 防止双击选中文本
            this.handleTap();
        });
        
        // 添加鼠标抬起事件，仅用于视觉反馈
        this.tapButton.addEventListener('mouseup', () => {
            // 可以在这里添加按钮的视觉反馈
        });
        
        // 添加触摸事件支持
        this.tapButton.addEventListener('touchstart', (e) => {
            e.preventDefault(); // 防止触摸设备上的延迟
            this.handleTap();
        });

        // 添加最后检测的拍子索引
        this.lastCheckedBeatIndex = -1;
        
        // 添加检测间隔的定时器
        this.checkMissedBeatsInterval = null;

        // 添加调试日志数组
        this.debugLogs = [];

        // 修改键盘事件监听
        document.addEventListener('keydown', (e) => {
            if (e.code === 'Space' && !e.repeat) { // 添加 !e.repeat 避免长按重复触发
                e.preventDefault();
                this.handleTap(); // 在按下时就触发点击检测
            }
        });

        // 键盘抬起事件只用于防止页面滚动
        document.addEventListener('keyup', (e) => {
            if (e.code === 'Space') {
                e.preventDefault();
            }
        });
    }

    async preloadAudio() {
        try {
            // 创建强拍音频
            this.strongBeatSynth = new ABCJS.synth.CreateSynth();
            const strongBeatAbc = `X:1\nL:1/64\nK:perc stafflines=1\nC16`;
            const strongBeatVisualObj = ABCJS.renderAbc('paper', strongBeatAbc)[0];
            await this.strongBeatSynth.init({
                audioContext: this.audioContext,
                visualObj: strongBeatVisualObj,
                options: {
                    program: 115,
                    soundFontUrl: "https://paulrosen.github.io/midi-js-soundfonts/FluidR3_GM/",
                    pan: [0]
                }
            });
            await this.strongBeatSynth.prime();

            // 创建弱拍音频
            this.weakBeatSynth = new ABCJS.synth.CreateSynth();
            const weakBeatAbc = `X:1\nL:1/64\nK:perc stafflines=1\nD16`;
            const weakBeatVisualObj = ABCJS.renderAbc('paper', weakBeatAbc)[0];
            await this.weakBeatSynth.init({
                audioContext: this.audioContext,
                visualObj: weakBeatVisualObj,
                options: {
                    program: 115,
                    soundFontUrl: "https://paulrosen.github.io/midi-js-soundfonts/FluidR3_GM/",
                    pan: [0]
                }
            });
            await this.weakBeatSynth.prime();

            console.log('音频预加载完成');
        } catch (error) {
            console.error('音频预加载失败:', error);
        }
    }

    async initABCJS() {
        try {
            // 创建音频上下文
            this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
            
            // 初始化 ABCJS 音频
            await ABCJS.synth.init({
                audioContext: this.audioContext,
                visualObj: null,
                millisecondsPerMeasure: (60000 / this.bpm) * 4
            });
            
            // 预加载音频
            await this.preloadAudio();
            
            console.log('ABCJS 初始化成功');
        } catch (error) {
            console.error('ABCJS 初始化失败:', error);
        }
    }

    createSynthControl() {
        // 创建合成器控制容器
        const synthControl = document.createElement('div');
        synthControl.id = 'synth-control';
        document.getElementById('paper').after(synthControl);

        // 初始化合成器控制器
        this.synth = new ABCJS.synth.CreateSynth();
    }

    bindEvents() {
        this.startStopButton.addEventListener('click', () => this.togglePlay());
        this.bpmInput.addEventListener('change', () => this.updateBPM());
        this.timeSignatureSelect.addEventListener('change', () => this.updateTimeSignature());
    }

    togglePlay() {
        if (this.isPlaying) {
            this.stop();
        } else {
            this.start();
        }
    }

    start() {
        this.isPlaying = true;
        this.startStopButton.textContent = '停止';
        this.startTime = performance.now();

        // 确保音频上下文是激活的
        if (this.audioContext.state === 'suspended') {
            this.audioContext.resume();
        }

        this.playBeat();
        const interval = (60 / this.bpm) * 1000;
        this.intervalId = setInterval(() => this.playBeat(), interval);

        // 修改检测间隔为四分之一拍的时间，以提高检测精度
        const checkInterval = (60 / this.bpm) * 250; // 四分之一拍
        this.lastCheckedBeatIndex = -1;
        this.checkMissedBeatsInterval = setInterval(() => this.checkMissedBeats(), checkInterval);
    }

    stop() {
        this.isPlaying = false;
        this.startStopButton.textContent = '开始';
        this.currentBeat = 0;
        this.startTime = null;  // 重置开始时间
        if (this.intervalId) {
            clearInterval(this.intervalId);
            this.intervalId = null;
        }
        this.tapTimes = [];  // 清空点击记录
        this.tapStartTime = null;

        // 清除未点击检测的定时器
        if (this.checkMissedBeatsInterval) {
            clearInterval(this.checkMissedBeatsInterval);
            this.checkMissedBeatsInterval = null;
        }
        this.lastCheckedBeatIndex = -1;

        // 导出调试日志
        this.exportDebugLogs();
        this.debugLogs = []; // 清空日志
    }

    updateBPM() {
        this.bpm = parseInt(this.bpmInput.value);
        // 清空预期时间显示
        const expectedTimesContainer = document.getElementById('expectedTimes');
        expectedTimesContainer.style.display = 'none';
        expectedTimesContainer.innerHTML = '';
        
        if (this.isPlaying) {
            this.stop();
            this.start();
        }
    }

    updateTimeSignature() {
        this.timeSignature = this.timeSignatureSelect.value;
        this.currentBeat = 0;
        // 清空预期时间显示
        const expectedTimesContainer = document.getElementById('expectedTimes');
        expectedTimesContainer.style.display = 'none';
        expectedTimesContainer.innerHTML = '';
        
        this.renderScore();
    }

    async playBeat() {
        const [numerator, denominator] = this.timeSignature.split('/').map(Number);
        
        // 计算从开始到现在经过的时间
        const elapsedTime = (performance.now() - this.startTime) / 1000;
        const timeString = formatTime(elapsedTime);
        
        // 确定是否为强拍
        const isStrongBeat = this.currentBeat === 0 || 
            (denominator === 8 && (numerator === 6 || numerator === 9 || numerator === 12) && this.currentBeat % 3 === 0) ||
            (numerator === 4 && this.currentBeat === 2) ||
            (numerator === 5 && this.currentBeat === 3) ||
            (numerator === 6 && this.currentBeat === 3);

        try {
            // 确保音频上下文已经启动
            if (this.audioContext.state === 'suspended') {
                await this.audioContext.resume();
            }

            // 使用预加载的音频播放
            if (isStrongBeat) {
                await this.strongBeatSynth.start();
            } else {
                await this.weakBeatSynth.start();
            }

            // 记录日志和更新显示
            const currentBar = Math.floor(this.currentBeat / numerator) + 1;
            const currentBeatInBar = (this.currentBeat % numerator) + 1;
            const beatType = isStrongBeat ? '强拍' : '弱拍';
            
            this.logBeat(timeString, currentBar, currentBeatInBar, beatType);
            this.renderPattern();
            this.highlightCurrentBeat();

            // 更新当前拍子位置
            this.currentBeat = (this.currentBeat + 1) % numerator;
        } catch (error) {
            console.error('播放失败:', error);
        }
    }

    // 新增方法：渲染完整节拍模式
    renderPattern() {
        const [numerator, denominator] = this.timeSignature.split('/').map(Number);
        let beatPattern = '';
        
        if (denominator === 8) {
            switch(numerator) {
                case 6: // 6/8
                    beatPattern = 'C8 D8 D8 C8 D8 D8';
                    break;
                case 9: // 9/8
                    beatPattern = 'C8 D8 D8 C8 D8 D8 C8 D8 D8';
                    break;
                case 12: // 12/8
                    beatPattern = 'C8 D8 D8 C8 D8 D8 C8 D8 D8 C8 D8 D8';
                    break;
                case 7: // 7/8
                    beatPattern = 'C8 D8 D8 D8 C8 D8 D8';
                    break;
                case 3: // 3/8
                    beatPattern = 'C8 D8 D8';
                    break;
                default:
                    beatPattern = 'C8' + ' D8'.repeat(numerator - 1);
            }
        } else {
            switch(numerator) {
                case 2: // 2/4 或 2/2
                    beatPattern = 'C16 D16';
                    break;
                case 3: // 3/4
                    beatPattern = 'C16 D16 D16';
                    break;
                case 4: // 4/4
                    beatPattern = 'C16 D16 D16 D16';
                    break;
                case 5: // 5/4
                    beatPattern = 'C16 D16 D16 C16 D16';
                    break;
                case 6: // 6/4
                    beatPattern = 'C16 D16 D16 C16 D16 D16';
                    break;
                default:
                    beatPattern = 'C16' + ' D16'.repeat(numerator - 1);
            }
        }

        // 渲染完整节拍模式
        ABCJS.renderAbc('paper', `X:1
L:1/64
M:${this.timeSignature}
K:perc stafflines=1
${beatPattern}`);
    }

    // 新增方法：渲染五线谱
    renderScore() {
        const [numerator, denominator] = this.timeSignature.split('/').map(Number);
        let scorePattern = '';
        
        // 生成一小节的音符
        if (denominator === 8) {
            // 8分音符为一拍的情况
            const notes = [];
            let currentBeat = 0;
            while (currentBeat < numerator) {
                if (currentBeat === 0 || (currentBeat % 3 === 0 && [6, 9, 12].includes(numerator))) {
                    notes.push('C,8'); // 重拍用低音C
                } else {
                    notes.push('E,8'); // 弱拍用低音E
                }
                currentBeat++;
            }
            scorePattern = notes.join(' ');
        } else {
            // 四分音符为一拍的情况
            const notes = [];
            for (let i = 0; i < numerator; i++) {
                if (i === 0 || // 第一拍
                    (numerator === 4 && i === 2) || // 4/4拍子的第三拍
                    ((numerator === 5 || numerator === 6) && i === 3)) { // 5/4和6/4的第四拍
                    notes.push('C,4'); // 重拍用低音C
                } else {
                    notes.push('E,4'); // 弱拍用低音E
                }
            }
            scorePattern = notes.join(' ');
        }

        // 生成完整的 ABC 记谱法字符串
        const scoreABC = `X:1
M:${this.timeSignature}
L:1/8
K:C
V:1 clef=treble
%%score (V1)
[V:1]
${scorePattern}|]`;

        // 渲染五线谱
        ABCJS.renderAbc('score', scoreABC, {
            add_classes: true,
            responsive: "resize",
            staffwidth: 500,
            scale: 1.5,
            paddingtop: 15,
            paddingbottom: 15
        });
    }

    // 新增方法：高亮当前拍子
    highlightCurrentBeat() {
        // 移除所有现有高亮
        document.querySelectorAll('.highlight-beat').forEach(el => {
            el.classList.remove('highlight-beat');
        });

        // 获取当前拍子的音符元素并添加高亮
        const noteElements = document.querySelectorAll('#score .abcjs-note');
        if (noteElements[this.currentBeat]) {
            noteElements[this.currentBeat].classList.add('highlight-beat');
        }
    }

    // 添加创建日志容器的方法
    createLogContainer() {
        const logContainer = document.createElement('div');
        logContainer.id = 'beat-log';
        logContainer.className = 'beat-log';
        document.getElementById('score').after(logContainer);
    }

    // 添加记录日志的方法
    logBeat(time, bar, beat, beatType) {
        const logContainer = document.getElementById('beat-log');
        const logEntry = document.createElement('div');
        logEntry.className = 'log-entry';
        logEntry.innerHTML = `<span class="time">${time}</span> - 第${bar}小节第${beat}拍 (${beatType})`;
        
        // 为小节的第一拍添加特殊样式
        if (beat === 1) {
            logEntry.style.color = '#2196F3';
            logEntry.style.fontWeight = 'bold';
        }
        
        logContainer.insertBefore(logEntry, logContainer.firstChild);
        
        while (logContainer.children.length > 50) {
            logContainer.removeChild(logContainer.lastChild);
        }
    }

    // 添加生成预期时间的方法
    generateExpectedTimes() {
        const [numerator] = this.timeSignature.split('/').map(Number);
        const beatInterval = 60 / this.bpm; // 每拍间隔（秒）
        const halfBeatInterval = beatInterval / 2; // 半拍时间
        const expectedTimesContainer = document.getElementById('expectedTimes');
        expectedTimesContainer.style.display = 'block';
        expectedTimesContainer.innerHTML = `
            <h3>预计每拍时间（从0开始）：</h3>
            <div class="time-header">
                <span>拍号位置</span>
                <span>预计时间</span>
                <span>点击检测时间段</span>
            </div>
        `;

        // 生成1000拍的时间
        for (let i = 0; i < 1000; i++) {
            const totalSeconds = i * beatInterval;
            
            // 计算检测时间段
            const detectionStart = totalSeconds - halfBeatInterval;
            const detectionEnd = totalSeconds + halfBeatInterval;

            // 格式化所有时间（使用外部的 formatTime 函数）
            const timeString = formatTime(totalSeconds);
            const startTimeString = formatTime(Math.max(0, detectionStart));
            const endTimeString = formatTime(detectionEnd);
            
            // 计算当前小节和拍子
            const currentBar = Math.floor(i / numerator) + 1;
            const currentBeat = (i % numerator) + 1;
            
            // 创建时间条目
            const timeEntry = document.createElement('div');
            timeEntry.className = 'time-entry';
            timeEntry.innerHTML = `
                <span class="beat-position">第${currentBar}小节第${currentBeat}拍</span>
                <span class="expected-time">${timeString}</span>
                <span class="detection-range">${startTimeString} ~ ${endTimeString}</span>
            `;
            
            // 为重拍添加特殊样式
            if (currentBeat === 1) {
                timeEntry.style.color = '#2196F3';
                timeEntry.style.fontWeight = 'bold';
            }
            
            expectedTimesContainer.appendChild(timeEntry);
        }

        // 滚动到顶部
        expectedTimesContainer.scrollTop = 0;
    }

    // 修改处理打拍子的方法
    handleTap() {
        // 如果节拍器正在播放，记录点击时间并进行检测
        if (this.isPlaying) {
            const tapTime = (performance.now() - this.startTime) / 1000; // 转换为秒
            this.logTap(tapTime);
            this.checkTapTiming(tapTime);
        }
    }

    // 记录打拍子的时间
    logTap(elapsedTime) {
        const timeString = formatTime(elapsedTime);
        const logContainer = document.getElementById('beat-log');
        const logEntry = document.createElement('div');
        logEntry.className = 'log-entry tap-entry';
        logEntry.innerHTML = `<span class="time">${timeString}</span> - 用户点击`;
        
        // 将点击记录插入到日志顶部
        logContainer.insertBefore(logEntry, logContainer.firstChild);
        
        // 限制日志条数
        while (logContainer.children.length > 50) {
            logContainer.removeChild(logContainer.lastChild);
        }
    }

    // 添加调试日志方法
    addDebugLog(message, data = {}) {
        const time = performance.now();
        const log = {
            time: time,
            relativeTime: this.startTime ? (time - this.startTime) / 1000 : 0,
            message,
            data
        };
        this.debugLogs.push(log);
        console.log(`[DEBUG] ${formatTime(log.relativeTime)} - ${message}`, data);
    }

    // 修改检查未点击的拍子方法
    checkMissedBeats() {
        if (!this.isPlaying) return;

        const currentTime = (performance.now() - this.startTime) / 1000;
        const beatInterval = 60 / this.bpm;
        const currentBeatIndex = Math.floor(currentTime / beatInterval);

        this.addDebugLog('检查漏拍', {
            currentTime,
            beatInterval,
            currentBeatIndex,
            lastCheckedBeatIndex: this.lastCheckedBeatIndex
        });

        // 检查是否有漏掉的拍子
        for (let i = this.lastCheckedBeatIndex + 1; i <= currentBeatIndex - 1; i++) {
            const beatTime = i * beatInterval;
            const nextBeatTime = (i + 1) * beatInterval;
            
            // 只有当当前时间已经超过了下一拍的开始时间，才检查是否漏拍
            if (currentTime >= nextBeatTime) {
                // 检查是否有任何点击落在这一拍的时间窗口内
                const halfBeatInterval = beatInterval / 2;
                const windowStart = beatTime - halfBeatInterval;
                const windowEnd = beatTime + halfBeatInterval;
                
                // 记录检查的时间窗口
                this.addDebugLog('检查时间窗口', {
                    beatIndex: i,
                    windowStart,
                    windowEnd,
                    currentTime
                });

                // 如果这一拍还没有被评分过，标记为 Miss
                if (!this.isCheckedBeat(i)) {
                    this.addDebugLog('发现漏拍', {
                        beatIndex: i,
                        beatTime,
                        currentTime,
                        timeDiff: currentTime - beatTime
                    });
                    this.logMissedBeat(i);
                    this.lastCheckedBeatIndex = i;
                }
            }
        }
    }

    // 新增：检查拍子是否已经被评分
    isCheckedBeat(beatIndex) {
        return beatIndex <= this.lastCheckedBeatIndex;
    }

    // 记录漏掉的拍子
    logMissedBeat(beatIndex) {
        const [numerator] = this.timeSignature.split('/').map(Number);
        const beatTime = beatIndex * (60 / this.bpm);
        const currentBar = Math.floor(beatIndex / numerator) + 1;
        const currentBeat = (beatIndex % numerator) + 1;

        const resultEntry = document.createElement('div');
        resultEntry.className = 'log-entry tap-result tap-miss';
        resultEntry.innerHTML = `
            <span class="time">${formatTime(beatTime)}</span> - 
            第${currentBar}小节第${currentBeat}拍 
            (Miss)
        `;
        
        const logContainer = document.getElementById('beat-log');
        logContainer.insertBefore(resultEntry, logContainer.firstChild);
    }

    // 修改检查点击时间的方法
    checkTapTiming(tapTime) {
        const [numerator] = this.timeSignature.split('/').map(Number);
        const beatInterval = 60 / this.bpm; // 每拍间隔（秒）
        
        // 找到最近的拍子
        const nearestBeatIndex = Math.round(tapTime / beatInterval);
        const nearestBeatTime = nearestBeatIndex * beatInterval;
        
        this.addDebugLog('检查点击', {
            tapTime,
            beatInterval,
            nearestBeatIndex,
            nearestBeatTime,
            lastCheckedBeatIndex: this.lastCheckedBeatIndex
        });

        // 如果这个拍子已经被评分过，跳过
        if (this.isCheckedBeat(nearestBeatIndex)) {
            this.addDebugLog('跳过评分 - 拍子已被评分', {
                nearestBeatIndex,
                lastCheckedBeatIndex: this.lastCheckedBeatIndex
            });
            return;
        }

        // 计算与最近拍子的时间偏差（毫秒）
        const deviation = Math.round((tapTime - nearestBeatTime) * 1000);
        const absDeviation = Math.abs(deviation);
        
        // 计算当前小节和拍子位置
        const currentBar = Math.floor(nearestBeatIndex / numerator) + 1;
        const currentBeat = (nearestBeatIndex % numerator) + 1;
        
        // 计算每拍时长（毫秒）
        const beatDuration = beatInterval * 1000;
        
        // 计算偏差百分比
        const deviationPercentage = (absDeviation / beatDuration) * 100;

        this.addDebugLog('点击评分计算', {
            deviation,
            absDeviation,
            beatDuration,
            deviationPercentage,
            currentBar,
            currentBeat
        });

        // 检查是否在有效的检测时间段内（半拍的时间窗口）
        const isInValidRange = deviationPercentage <= 50; // 半拍 = 50%

        // 如果不在有效范围内，直接判定为 Miss
        if (!isInValidRange) {
            const resultEntry = document.createElement('div');
            resultEntry.className = 'log-entry tap-result tap-miss';
            resultEntry.innerHTML = `
                <span class="time">${formatTime(tapTime)}</span> - 
                第${currentBar}小节第${currentBeat}拍 
                (超出检测范围: ${deviation > 0 ? '延迟' : '提前'}${absDeviation}ms, ${deviationPercentage.toFixed(1)}%) 
                Miss
            `;
            
            const logContainer = document.getElementById('beat-log');
            logContainer.insertBefore(resultEntry, logContainer.firstChild);
            return;
        }
        
        // 根据偏差百分比确定评级
        let rating;
        let ratingClass;
        if (deviationPercentage <= 8) {           // 偏差 ≤ 8%
            rating = 'Perfect';
            ratingClass = 'tap-perfect';
        } else if (deviationPercentage <= 12) {   // 8% < 偏差 ≤ 12%
            rating = 'Great';
            ratingClass = 'tap-great';
        } else if (deviationPercentage <= 16) {   // 12% < 偏差 ≤ 16%
            rating = 'Good';
            ratingClass = 'tap-good';
        } else {                                  // 偏差 > 16%
            rating = 'Miss';
            ratingClass = 'tap-miss';
        }
        
        // 添加点击结果到日志
        const resultEntry = document.createElement('div');
        resultEntry.className = `log-entry tap-result ${ratingClass}`;
        resultEntry.innerHTML = `
            <span class="time">${formatTime(tapTime)}</span> - 
            第${currentBar}小节第${currentBeat}拍 
            (${deviation > 0 ? '延迟' : '提前'}${absDeviation}ms, ${deviationPercentage.toFixed(1)}%) 
            ${rating}
        `;
        
        const logContainer = document.getElementById('beat-log');
        logContainer.insertBefore(resultEntry, logContainer.firstChild);

        // 更新最后检测的拍子索引
        this.lastCheckedBeatIndex = Math.max(this.lastCheckedBeatIndex, nearestBeatIndex);
    }

    // 添加导出调试日志的方法
    exportDebugLogs() {
        const logText = this.debugLogs.map(log => {
            return `${formatTime(log.relativeTime)} - ${log.message}\n${JSON.stringify(log.data, null, 2)}`;
        }).join('\n\n');

        const blob = new Blob([logText], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `metronome-debug-${new Date().toISOString()}.txt`;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }
}

// 创建节拍器实例
const metronome = new Metronome(); 